package com.lvtulife.common.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 日期工具类
 *
 * @author lvtulife
 */
public class DateUtil {

    public final static Logger logger = LoggerFactory.getLogger(DateUtil.class);
    public static final String YYYY_MM = "yyyy-MM";
    public static final String YYYY_MM_DD = "yyyy-MM-dd";
    public static final String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    public static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String ZH_YYYY_MM_DD_HH_MM = "yyyy年MM月dd日HH时mm分";
    public static final String ZH_YYYY_MM_DD_HH_MM_SS = "yyyy年MM月dd日HH时mm分ss秒";
    public static final String NO_LINE_YYYY_MM_DD_HH_MM_SS = "yyyyMMddHHmmss";
    public static final String NO_LINE_YYYY_MM_DD_HH_MM_SS_SSS = "yyyyMMddHHmmssSSS";
    public static final String NO_LINE_YYYY_MM_DD = "yyyyMMdd";
    public static final String NO_LINE_YY_MM_DD = "yyMMdd";
    public static final String YYYY = "yyyy";
    /**
     * JDK 1.8 新特性 end
     */
    private static Calendar c = Calendar.getInstance();

    //日期常量
    enum DateConstants {
        TODAY(0), NEARLYWEEK(1), MONTH(2), NEARLYMONTH(3);
        public int value;

        DateConstants(int value) {
            this.value = value;
        }
    }

    public static DateFormat YYYY_format;
    public static DateFormat YYYY_MM_format;
    public static DateFormat YYYY_MM_DD_format;
    public static DateFormat YYYY_MM_DD_HH_MM_format;
    public static DateFormat YYYY_MM_DD_HH_MM_SS_format;
    public static DateFormat YYYY_MM_DD_HH_MM_SS_SSS_format;
    public static DateFormat ZH_YYYY_MM_DD_HH_MM_format;
    public static DateFormat ZH_YYYY_MM_DD_HH_MM_SS_format;
    public static DateFormat NO_LINE_YYYY_MM_DD_HH_MM_SS_format;
    public static DateFormat NO_LINE_YYYY_MM_DD_HH_MM_SS_SSS_format;
    public static DateFormat NO_LINE_YYYY_MM_DD_format;
    public static DateFormat NO_LINE_YY_MM_DD_format;

    static {
        YYYY_format = SimpleDateFormatFactory.getInstance(YYYY);
        YYYY_MM_format = SimpleDateFormatFactory.getInstance(YYYY_MM);
        YYYY_MM_DD_format = SimpleDateFormatFactory.getInstance(YYYY_MM_DD);
        YYYY_MM_DD_HH_MM_format = SimpleDateFormatFactory.getInstance(YYYY_MM_DD_HH_MM);
        YYYY_MM_DD_HH_MM_SS_format = SimpleDateFormatFactory.getInstance(YYYY_MM_DD_HH_MM_SS);
        YYYY_MM_DD_HH_MM_SS_SSS_format = SimpleDateFormatFactory.getInstance(YYYY_MM_DD_HH_MM_SS_SSS);
        ZH_YYYY_MM_DD_HH_MM_format = SimpleDateFormatFactory.getInstance(ZH_YYYY_MM_DD_HH_MM);
        ZH_YYYY_MM_DD_HH_MM_SS_format = SimpleDateFormatFactory.getInstance(ZH_YYYY_MM_DD_HH_MM_SS);
        NO_LINE_YYYY_MM_DD_HH_MM_SS_format = SimpleDateFormatFactory.getInstance(NO_LINE_YYYY_MM_DD_HH_MM_SS);
        NO_LINE_YYYY_MM_DD_HH_MM_SS_SSS_format = SimpleDateFormatFactory.getInstance(NO_LINE_YYYY_MM_DD_HH_MM_SS_SSS);
        NO_LINE_YYYY_MM_DD_format = SimpleDateFormatFactory.getInstance(NO_LINE_YYYY_MM_DD);
        NO_LINE_YY_MM_DD_format = SimpleDateFormatFactory.getInstance(NO_LINE_YY_MM_DD);
    }

    public static void main(String[] args) {
        System.out.println(getTimebyMinAfter(20));
        System.out.println(getTimebySecAfter(-20));
    }


    /**************/
    /***时间推算***/
    /**************/

    /* 获取当前时间在＋－n分钟后的字符串时间 */
    public static String getTimebyMinAfter(int n) {
        return (YYYY_MM_DD_HH_MM_SS_format.format(getTimebyMinAfterDate(n)));
    }

    /*获取当前时间在＋－n分钟后的时间(java.util.Date)*/
    public static Date getTimebyMinAfterDate(int n) {
        Calendar now = Calendar.getInstance(TimeZone.getDefault());
        now.add(Calendar.MINUTE, n);
        return now.getTime();
    }

    /* 获取当前时间在＋－n秒后的字符串时间 */
    public static String getTimebySecAfter(int n) {
        Calendar now = Calendar.getInstance(TimeZone.getDefault());
        now.add(Calendar.SECOND, n);
        return (YYYY_MM_DD_HH_MM_SS_format.format(now.getTime()));
    }


    /* 根据失效时间判断是否依然有效,失效时间要求:yyyyMMddHHmmss,true:失效 false:没有失效 */
    public static boolean isTimeExpired(String expireTime) throws ParseException {
        boolean ret = false;
        Calendar expire = Calendar.getInstance();
        expire.setTime(NO_LINE_YYYY_MM_DD_HH_MM_SS_format.parse(expireTime));
        Calendar now = Calendar.getInstance();
        // 将毫秒字段设为0,保持精度一致性
        now.set(Calendar.MILLISECOND, 0);
        if (now.after(expire)) {
            ret = true;
        }
        return ret;
    }

    /* 根据开始时间和可用时间计算出失效时间 */
    public static String getExpireTimeByCalculate(Date startTime, int availableTime) {
        Calendar expire = Calendar.getInstance();
        expire.setTime(startTime); // 设置为开始时间
        expire.add(Calendar.SECOND, availableTime);// 有效时长（单位：秒）开始时间向后有效时长秒得到失效时间
        return NO_LINE_YYYY_MM_DD_HH_MM_SS_format.format(expire.getTime());
    }


    /****************/
    /****格式转换****/
    /****************/

    /* 将Date转换成“yyyy-mm-dd hh:mm:ss”的字符串 */
    public static String dateFormat_YYYY_MM_DD_HH_MM_SS(Date date) {
        return YYYY_MM_DD_HH_MM_SS_format.format(date);
    }

    /* 将Date转换成 "yyyy-MM-dd" 的字符串 */
    public static String dateFormat_YYYY_MM_DD(Date date) {
        return YYYY_MM_DD_format.format(date);
    }

    /* 将Date转换成 "yyyy年MM月dd日HH时mm分ss秒" 的字符串 */
    public static String dateFormat_ZH_YYYY_MM_DD_HH_MM_SS(Date date) {
        return ZH_YYYY_MM_DD_HH_MM_SS_format.format(date);
    }

    /* 将Date转换成 "yyyyMMddHHmmss" 的字符串 */
    public static String dateFormat_NO_LINE_YYYY_MM_DD_HH_MM_SS(Date date) {
        return NO_LINE_YYYY_MM_DD_HH_MM_SS_format.format(date);
    }


    /* 获取今天的日期 */
    public static String getToday_YYYY_MM_DD_HH_MM_SS() {
        return dateFormat_YYYY_MM_DD_HH_MM_SS(new Date());
    }

    /* 获取今天的日期 */
    public static String getToday_ZH_YYYY_MM_DD_HH_MM_SS() {
        return dateFormat_ZH_YYYY_MM_DD_HH_MM_SS(new Date());
    }


    /* 将yyyy-MM-dd格式的日期字符换成Date的形式 */
    public static Date convert_YYYY_MM_DD(String date) throws ParseException {
        return YYYY_MM_DD_format.parse(date);
    }

    /* 将yyyy-MM-dd格式的日期字符换成Date的形式 */
    public static Date convert_YYYY_MM_DD_HH_MM_SS(String date) throws ParseException {
        return YYYY_MM_DD_HH_MM_SS_format.parse(date);
    }

    /* 将yyyyMMddHHmmss格式的日期字符转换为yyyy年MM月dd日HH时mm分ss秒格式的字符串 */
    public static String convert_ZH_YYYY_MM_DD_HH_MM_SS(String date) throws ParseException {
        return ZH_YYYY_MM_DD_HH_MM_SS_format.format(NO_LINE_YYYY_MM_DD_HH_MM_SS_format.parse(date));
    }

    /* 获取系统当前的分钟数,如当前时间是上午12:00,系统当前的分钟数就是12*60 */
    public static int getCurrentMinutes() {
        Calendar now = Calendar.getInstance(TimeZone.getDefault());
        return now.get(Calendar.HOUR_OF_DAY) * 60 + now.get(Calendar.MINUTE);
    }


    /* 返回年份. */
    public static String getYear() {
        return YYYY_format.format(new Date());
    }

    /* 获取本月月份 */
    public static String getCurrentMonth(boolean addZero) {
        Calendar now = Calendar.getInstance();
        return getMonth(null, Calendar.MONTH, addZero);
    }

    /* 获取指定日期所在月份 */
    public static String getDayForMonth(Date date, boolean addZero) {
        return getMonth(date, Calendar.MONTH, addZero);
    }

    /* 获取本月第几天 */
    public static String getDayOfMonth(boolean addZero) {
        return getMonth(null, Calendar.DAY_OF_MONTH, addZero);
    }

    private static String getMonth(Date date, int field, boolean addZero) {
        Calendar now = Calendar.getInstance();

        // 当前时间 date 留空
        if (date != null) {
            now.setTime(date);
        }

        int value = 1;
        if (Calendar.MONTH == field) {
            value = now.get(Calendar.MONTH) + 1;
        } else if (Calendar.DAY_OF_MONTH == field) {
            value = now.get(Calendar.DAY_OF_MONTH);
        }

        String str = String.valueOf(value);
        // 小于10的在前面加"0"
        if (addZero && value < 10)
            str = "0" + str;
        return str;
    }

    /* 获取昨日日期,并转换成日期格式字符串  */
    public static String getYesterdayDateStr() {
        return YYYY_MM_DD_format.format(getYesterdayDate());
    }

    /* 获取明天日期,并转换成日期格式字符串 */
    public static String getTomorrowDateStr() {
        return YYYY_MM_DD_format.format(getTomorrowDate());
    }

    /* 获取昨日日期,并转换成日期格式字符串  */
    public static Date getYesterdayDate() {
        return getBeforeOrAfterDate(1, true, null);
    }

    /* 获取明天日期,并转换成日期格式字符串 */
    public static Date getTomorrowDate() {
        return getBeforeOrAfterDate(1, false, null);
    }

    /* 日期前后推算 */
    public static Date getBeforeOrAfterDate(int size, boolean before, Date protoDate) {
        return getBeforeOrAfter(Calendar.DAY_OF_MONTH, size, before, protoDate);
    }

    /* 月份前后推算 */
    public static Date getBeforeOrAfterMonth(int size, boolean before, Date protoDate) {
        return getBeforeOrAfter(Calendar.MONTH, size, before, protoDate);
    }

    /* 时间前后推算 */
    public static Date getBeforeOrAfterHOUR(int size, boolean before, Date protoDate) {
        return getBeforeOrAfter(Calendar.HOUR_OF_DAY, size, before, protoDate);
    }

    /* 前后推算 */
    public static Date getBeforeOrAfter(int field, int size, boolean before, Date protoDate) {
        Calendar cal = Calendar.getInstance();
        if (protoDate != null) {
            cal.setTime(protoDate);
        }
        cal.add(field, before ? -size : size);
        return cal.getTime();
    }

    /**
     * 获取指定月份和日子的日期(未做日期效验)
     *
     * @param currentDate 当前日期
     * @param assignYear  指定年份,-1代表年不做修改
     * @param assignMonth 指定月份,从0开始,超过月最大值自动往后加一个月年,-1代表月不做修改
     * @param assignDay   指定日,从1开始,超过日最大值往后加一个月,-1代表日不做修改
     * @return 修改后的日期
     */
    public static Date getAssignDate(Date currentDate, int assignYear, int assignMonth, int assignDay) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(currentDate);
        if (assignYear > -1) {
            cal.set(Calendar.YEAR, assignYear);
        }
        if (assignMonth > -1) {
            cal.set(Calendar.MONTH, assignMonth);
        }
        if (assignDay > -1) {
            cal.set(Calendar.DAY_OF_MONTH, assignDay);
        }
        return cal.getTime();
    }

    /* 获得两个日前之间相差的天数,有时分秒的影响 */
    public static int getDayCountBetweenDate(Date startDate, Date endDate) {
        Calendar startCalendar = Calendar.getInstance();
        startCalendar.setTime(startDate);
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(endDate);
        int i = 0;
        while (endCalendar.compareTo(startCalendar) >= 0) {
            startCalendar.set(Calendar.DAY_OF_MONTH, startCalendar.get(Calendar.DAY_OF_MONTH) + 1);
            i++;
        }
        return i;
    }

    /* 获得两个日前之间相差的月份 */
    public static int getMonthCountBetweenDate(Date startDate, Date endDate) {
        Calendar startCalendar = Calendar.getInstance();
        startCalendar.setTime(startDate);
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(endDate);
        int i = 0;
        while (endCalendar.compareTo(startCalendar) >= 0) {
            startCalendar.set(Calendar.MONTH, startCalendar.get(Calendar.MONTH) + 1);
            i++;
        }
        return i;
    }

    /* 获得两个日前之间相差的年 */
    public static int getYearlyCountBetweenDate(Date startDate, Date endDate) {
        Calendar startCalendar = Calendar.getInstance();
        startCalendar.setTime(startDate);
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(endDate);
        int i = 0;
        while (endCalendar.compareTo(startCalendar) >= 0) {
            startCalendar.set(Calendar.YEAR, startCalendar.get(Calendar.YEAR + 1));
            i++;
        }
        return i;
    }

    /* 获取这个月第一天 */
    public static Date getFirstDayOfMonth() {
        Calendar ca = Calendar.getInstance();
        ca.set(Calendar.DAY_OF_MONTH, 1);
        Date firstDate = ca.getTime();
        ca.add(Calendar.MONTH, 1);
        ca.add(Calendar.DAY_OF_MONTH, -1);
        return firstDate;
    }

    /* 获这个月的最后一天 */
    public static Date getLastDayOfMonth() {
        Calendar ca = Calendar.getInstance();
        ca.set(Calendar.DAY_OF_MONTH, 1);
        ca.add(Calendar.MONTH, 1);
        ca.add(Calendar.DAY_OF_MONTH, -1);
        Date lastDate = ca.getTime();
        return lastDate;
    }

    /**
     * JDK 1.8 新特性 LocalDateTime   start
     */
    public static LocalDateTime now() {
        return LocalDateTime.now();
    }

    public static String toDateTime(LocalDateTime date) {
        return toDateTime(date, YYYY_MM_DD_HH_MM_SS);
    }

    public static String toDateTime(LocalDateTime dateTime, String pattern) {
        return dateTime.format(DateTimeFormatter.ofPattern(pattern, Locale.SIMPLIFIED_CHINESE));
    }

    public static String toDateText(LocalDate date, String pattern) {
        if (date == null || pattern == null) {
            return null;
        }
        return date.format(DateTimeFormatter.ofPattern(pattern, Locale.SIMPLIFIED_CHINESE));
    }



    /**
     * 日期转字符串
     *
     * @param date    日期
     * @param pattern 格式
     * @return
     */
    public static String dateToString(Date date, String pattern) {
        if (date != null) {
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            return sdf.format(date);
        }
        return "";
    }

    /**
     * 日期转字符串
     *
     * @param date
     * @return
     */
    public static String dateToString(Date date) {
        return dateToString(date, YYYY_MM_DD_HH_MM_SS);
    }


    /**
     * 根据当前日期currDate得到往前数weeks周数之后的日期，比如输入currDate=2007-01-24 09:43:26,weeks=2, 则返回日期为2007-01-10 09:43:26。
     *
     * @param currDate
     * @param weeks
     * @return
     * @author 裴伟斌 2007-1-24
     */
    public static Date getDateBeforeWeeks(Date currDate, int weeks) {
        if (null == currDate)
            return null;
        Calendar working = (Calendar) c.clone();
        working.setTime(currDate);
        working.add(Calendar.WEEK_OF_YEAR, -weeks);
        Date newDate = working.getTime();
        return newDate;
    }


    /**
     * 获取当前日期是星期几
     *
     * @param dt
     * @return
     */
    public static String getWeekOfDate(Date dt) {
        String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(dt);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0)
            w = 0;
        return weekDays[w];
    }


    /**
     * 验证时间格式是否符合
     *
     * @param pattern 需要匹配的时间格式
     * @param timeStr 时间值
     * @return <code>boolean</code> 返回<code>true</code>表示时间格式匹配 返回 <code>false</code>表示时间格式不匹配
     */
    public static boolean match(String pattern, String timeStr) {

        // 修改发送时间
        Pattern patternObj = Pattern.compile(pattern);
        Matcher matcher = patternObj.matcher(timeStr);
        boolean result = matcher.matches();
        return result;

    }

    public static String getMonday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int week = cal.get(Calendar.DAY_OF_WEEK);
        if (week > 2) {
            cal.add(Calendar.DAY_OF_MONTH, -(week - 2));
        } else {
            cal.add(Calendar.DAY_OF_MONTH, 2 - week);
        }

        DateFormat shortDateFormat = DateFormat.getDateInstance(0);
        String dateStr = shortDateFormat.format(cal.getTime());

        return dateStr;
    }

    public static Date getSunday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int week = cal.get(Calendar.DAY_OF_WEEK);
        if (week > 1) {
            cal.add(Calendar.DAY_OF_MONTH, -(week - 1));
        } else {
            cal.add(Calendar.DAY_OF_MONTH, 1 - week);
        }
        return cal.getTime();
    }

    public static String getNextMonday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int week = cal.get(Calendar.DAY_OF_WEEK);
        if (week > 2) {
            cal.add(Calendar.DAY_OF_MONTH, -(week - 2) + 7);
        } else {
            cal.add(Calendar.DAY_OF_MONTH, 2 - week + 7);
        }

        DateFormat shortDateFormat = DateFormat.getDateInstance(0);
        String dateStr = shortDateFormat.format(cal.getTime());
        return dateStr;
    }

    public static Date getNextSunday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int week = cal.get(Calendar.DAY_OF_WEEK);
        if (week > 1) {
            cal.add(Calendar.DAY_OF_MONTH, -(week - 1) + 7);
        } else {
            cal.add(Calendar.DAY_OF_MONTH, 1 - week + 7);
        }
        return cal.getTime();
    }

    public static int getMaxDaysOfYear(int year) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        return cal.getActualMaximum(Calendar.DAY_OF_YEAR);
    }

    public static int getMaxDaysOfMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, year);
        return cal.getActualMaximum(Calendar.DAY_OF_MONTH);
    }
}
